#include "modbus.h"

struct list_head head; //内核链表头节点
extern modbus_t *ctx;  // Modbus上下文
extern char ip_address[16]; //modbus设备IP地址
extern int port;   //modbus设备端口号

// 解析Modbus配置文件并构建缓存链表填充
int parse_modbus_config(void)
{
    // 打开文件
    FILE *fp = fopen("./node.json","r");
    if (!fp)
    {
        perror("文件打开失败");
        return -1;
    }
    // 读取文件内容
    char buf[20000] = "";
    fread(buf, 20000, 1, fp);
    fclose(fp);
    // 解析JSON
    cJSON *root = cJSON_Parse(buf);
    if (!root)
    {
        printf("JSON解析错误: %s\n", cJSON_GetErrorPtr());
        return -1;
    }
    // 获取数据点数组
    cJSON *mb_dev = cJSON_GetObjectItem(root,"mb_dev");
    strcpy(ip_address,cJSON_GetObjectItem(mb_dev,"addr")->valuestring);
    port = cJSON_GetObjectItem(mb_dev,"port")->valueint;
    cJSON *data = cJSON_GetObjectItem(cJSON_GetObjectItem(root,"modbus"), "data");
    // 内核链表初始化
    INIT_LIST_HEAD(&head);
    // 构建内核缓存链表并填充节点(方便快速访问,减少点表重复解析)
    int count = cJSON_GetArraySize(data);
    struct mb_node *nodes = malloc(count*sizeof(struct mb_node));
    if (!nodes)
    {
        perror("内存分配失败");
        free(&head);
        cJSON_Delete(root);
        return -1;
    }
    // 解析每个数据点
    for (int i = 0; i < count; i++)
    {
        cJSON *item = cJSON_GetArrayItem(data, i);
        struct mb_node *node = &nodes[i];
        node->key = cJSON_GetObjectItem(item,"key")->valueint;
        strcpy(node->name, cJSON_GetObjectItem(item,"name")->valuestring);
        node->type = cJSON_GetObjectItem(item,"type")->valueint;
        node->addr = cJSON_GetObjectItem(item,"addr")->valueint;
        list_add_tail(&node->list, &head);
    }
    cJSON_Delete(root);
    return 0;
}
//  Modbus TCP连接
modbus_t* init_modbus_tcp_connection(const char* ip_address, int port, int slave_id)
{
    ctx = modbus_new_tcp(ip_address, port);
    if (ctx == NULL)
    {
        perror("创建Modbus TCP上下文失败");
        return NULL;
    }
    // 设置从机ID(默认1)
    if (modbus_set_slave(ctx, slave_id) != 0)
    {
        perror("设置从机ID失败");
        modbus_free(ctx);
        return NULL;
    }
    // 连接Modbus设备
    if (modbus_connect(ctx) != 0)
    {
        perror("连接Modbus设备失败");
        modbus_free(ctx);
        return NULL;
    }
    printf("成功建立Modbus TCP连接 - IP: %s, 端口: %d, 从机ID: %d\n",
           ip_address, port, slave_id);
    return ctx;
}
// 数据采集线程(负责从Modbus设备读取传感器数据)
void *collect(void *arg)
{
    // 创建并初始化共享内存
    struct shm_param para;
    if(shm_init(&para, "shm", 1024) < 0)
    {
        printf("shm init err\n");
        return NULL;
    }
    // 获取共享内存地址
    int *p=(char *)shm_getaddr(&para);
    if(p == NULL)
    {
        printf("shm get addr\n");
        return NULL;
    }
    while(1)
    {
        sleep(1);  // 每秒采集一次
        //遍历内核链表
        struct list_head *pos=&head;
        struct mb_node *tmp;
        list_for_each(pos,&head)
        {
            struct std_node *st=(struct std_node *)(p+1);
            tmp = list_entry(pos,struct mb_node,list);
            for(int i=0;i<*p;i++)
            {
                if(st->key==tmp->key)
                    break;
                st++;
            }
            st->key=tmp->key;
            st->type=tmp->type;
            st->dev_type=2;  //dev_type=2表示为modbus设备
            st->old_val=st->new_val;
            st->ret=read_modbus_data(tmp,&st->new_val);
        }
    }
    shm_del(&para);
}
// modbus设备控制线程(负责接收控制指令并写入Modbus设备)
void *control(void *arg)
{
    struct control msg;
    while(1)
    {
        printf("等待接收消息队列指令...\n");
        int recv_rc = msg_queue_recv("control", &msg, sizeof(struct control), 2, 0);
        if (recv_rc == -1) {
            printf("消息队列接收失败\n");
            continue;
        }
        printf("接收到控制指令，K值:%d\n", msg.key);
        // 遍历内核链表找对应key的节点
        struct list_head *pos=&head;
        struct mb_node *tmp = NULL;
        list_for_each(pos, &head)
        {
            tmp = list_entry(pos, struct mb_node, list);
            if (msg.key == tmp->key)
            {
                break;
            }
            tmp = NULL; // 没找到时置空
        }
        if (tmp)
        {
            if (write_modbus_data(tmp, &msg.val) == -1)
            {
                printf("写入失败: %s\n", modbus_strerror(errno));
            }
        } else {
            printf("未找到K值为%d的节点，无法写入\n", msg.key);
        }
    }
    return NULL;
}
//根据链表数据点配置(寄存器地址地址)读取寄存器数据
int read_modbus_data(struct mb_node *node, union val_t *data)
{
    int rc = -1;
    uint16_t reg_addr = 0;
    uint16_t dest[2] = {0};
    int protocol_addr = node->addr;
    int reg_type = 0; // 0:输入寄存器(3x) 1:保持寄存器(4x) 2:线圈(0x)
    // 1. 协议地址分类 & 寄存器类型判断
    if (protocol_addr >= 30001 && protocol_addr <= 39999) {
        reg_type = 0;
        reg_addr = protocol_addr - 30001;
    } else if (protocol_addr >= 40001 && protocol_addr <= 49999) {
        reg_type = 1;
        reg_addr = protocol_addr - 40001;
    } else if (protocol_addr >= 1 && protocol_addr <= 9999) {
        reg_type = 2;
        reg_addr = protocol_addr - 1;
    } else {
        printf("错误：无效的协议地址 %d\n", protocol_addr);
        return -1;
    }
    // 2. 统一读取逻辑（根据寄存器类型和数据类型）
    if (reg_type == 0 || reg_type == 1) { // 输入/保持寄存器
        int read_count;
        // 判断读取数量
        if ((node->type & 0x0F) == 3) {
            read_count = 2; // float 读 2 个
        } else {
            read_count = 1; // 其他类型读 1 个
        }
        // 根据寄存器类型选择读取函数
        if (reg_type == 0) {
            rc = modbus_read_input_registers(ctx, reg_addr, read_count, dest);
        } else {
            rc = modbus_read_registers(ctx, reg_addr, read_count, dest);
        }
        // float 类型特殊处理（整数+小数组合）
        if ((node->type & 0x0F) == 3) {
            if (rc == 2) {
                data->f_val = dest[0] + (dest[1] / 100.0f);
                printf("K值:%d,new_val:%.2f\n",node->key,data->f_val);
                return 0;
            } else {
                printf("错误：float 需 2 个寄存器，实际读取 %d 个\n",rc);
                return -1;
            }
        }
    } else { // 线圈
        rc = modbus_read_bits(ctx, reg_addr, 1, dest);
    }
    // 3. 通用错误检查
    if (rc == -1) {
        printf("读取失败: %s\n", modbus_strerror(errno));
        return -1;
    }
    // 4. 非 float 类型解析（合并条件判断）
    if ((node->type & 0x0F) != 3) {
        switch (node->type & 0x0F) {
            case 1: // bool
                data->b_val = dest[0] ? 1 : 0;
                printf("K值:%d,new_val:%d\n",node->key,data->b_val);
                break;
            case 2: // int
                data->i_val = dest[0];
                printf("K值:%d,new_val:%d\n",node->key,data->i_val);
                break;
            default:
                printf("错误：未知数据类型 %d\n", node->type & 0x0F);
                return -1;
        }
    }
    return rc;
}
//根据消息队列中指令(K值和数据)写入目标寄存器
int write_modbus_data(struct mb_node *node, union val_t *data)
{
    int rc = -1;
    uint16_t reg_addr = 0;
    // 根据低4位解析数据类型
    switch (node->type & 0x0F)
    {
        case 1:  // bool类型
            // 写入单个线圈 (功能码05)
            reg_addr = node->addr - 1; // 线圈地址转换
            uint8_t coil_value = data->b_val ? 1 : 0; // 修正，线圈值应为0或1
            rc = modbus_write_bit(ctx, reg_addr, coil_value);
            break;
        case 2:  // int型
            // 写入单个保持寄存器 (功能码06)
            reg_addr = node->addr - 40001; // 保持寄存器地址转换
            uint16_t int_value = (uint16_t)data->i_val;
            rc = modbus_write_register(ctx, reg_addr, int_value);
            break;
        case 3:  // float型
    // 保持寄存器地址转换（Modbus 地址如 40001 转换为 0 基址）
    reg_addr = node->addr - 40001;
    // 拆分到两个 16 位寄存器（大端序）
    uint16_t float_regs[2];
    float_regs[0] = data->f_val; // 高 16 位
    float_regs[1] = (data->f_val - float_regs[0] )*100;  // 低 16 位
    // 调试输出：打印原始值、二进制及拆分结果
    printf("写入 float 值: %.6f\n", data->f_val);
    printf("寄存器 1 (高 16 位): %d\n", float_regs[0]);
    printf("寄存器 2 (低 16 位): %d\n", float_regs[1]);
    // 写入两个保持寄存器（功能码 10）
    rc = modbus_write_registers(ctx, reg_addr, 2, float_regs);
    if (rc == 2) { // 成功写入 2 个寄存器
        printf("Float 写入成功，地址: %d 和 %d\n",
               node->addr, node->addr + 1);
    } else {
        printf("Float 写入失败，错误码: %d\n", rc);
        return -1;
    }
    break;
        default:
            printf("数据类型错误: %d\n", node->type & 0x0F);
            return -1;
    }
    if (rc == -1)
    {
        printf("写入失败: %s\n", modbus_strerror(errno));
        return -1;
    }
    // 根据数据类型选择合适的格式化输出
    switch (node->type & 0x0F) {
        case 1: // bool类型
            printf("写入成功: 地址=%d, 类型=%d, 值=%d\n",
                   node->addr, node->type & 0x0F, data->b_val);
            break;
        case 2: // int类型
            printf("写入成功: 地址=%d, 类型=%d, 值=%d\n",
                   node->addr, node->type & 0x0F, data->i_val);
            break;
        case 3: // float类型
            printf("写入成功: 地址=%d, 类型=%d, 值=%f\n",
                   node->addr, node->type & 0x0F, data->f_val);
            break;
        default:
            break;
    }
    return 0;
}
